package com.h2.ref.server.user;

import java.util.ArrayList;
import java.util.Collection;

import org.springframework.dao.DataAccessException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.util.StringUtils;

import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.h2.ref.server.auth.Authz;
import com.h2.ref.server.user.dao.IUserDao;
import com.h2.ref.server.user.model.User;
import com.h2.ref.server.util.sec.PasswordEncoder;
import com.h2.ref.server.util.sec.TokenGenerator;

public class UserService implements UserDetailsService, IUserService {

   private IUserDao _userDao;
   
   private Collection<IUserListener> _userListeners =
         new ArrayList<IUserListener>();
   
   ////
   ////
   
   /* (non-Javadoc)
    * @see com.h2.tac.server.user.IUserService#getActiveUser()
    */
   @Override
   public User getActiveUser() {
      String username = Authz.getActiveUser();
      return getUserDao().findByUserName(username);
   }
   
   @Override
   public User getActiveUser(Boolean password) {
      String username = Authz.getActiveUser();
      User result = getUserDao().findByUserName(username);
      if (result != null) {
         result.setPassword(null);
      }
      return result;
   }

   @Override
   public void update(User user) {
      getUserDao().update(user);
      
      //send events
      for (IUserListener listener : getUserListeners()) {
         listener.onDeleteUser(user);
      }
   }

   @Override
   public void delete(User user) {
      getUserDao().delete(user);
      
      //send events
      for (IUserListener listener : getUserListeners()) {
         listener.onDeleteUser(user);
      }
   }

   /* (non-Javadoc)
    * @see org.springframework.security.core.userdetails.UserDetailsService#
    *       loadUserByUsername(java.lang.String)
    */
   @Override
   public UserDetails loadUserByUsername(String username)
         throws UsernameNotFoundException, DataAccessException {
      User user = findByUserName(username);
      if(user == null) {
         return null;
      }
      return UserExt.createUserExt(user);
   }
   
   @Override
   public User find(User user) {
      return findByUserId(user.getId());
   }
   
   /* (non-Javadoc)
    * @see com.h2.tac.server.user.IUserService#findByUserId(java.lang.Integer)
    */
   @Override
   public User findByUserId(Integer userId) {
      return getUserDao().findByUserId(userId);
   }
   
   @Override
   public User findByUserName(String name) {
      return getUserDao().findByUserName(name);
   }
   
   @Override
   public User findByEmail(String email) {
      return getUserDao().findByEmail(email);
   }

   @Override
   public User search(String name) {
      return findByUserName(name);
   }
   
   /* (non-Javadoc)
    * @see com.h2.tac.server.user.IUserService#addRegisteredUser(
    *    com.h2.tac.user.model.User)
    */
   @Override
   public User addRegisteredUser(User user, Boolean active) {
      
      //roles
      if (user.getRoles() == null) {
         user.setRoles(new ArrayList<String>());
      }
      if (!user.getRoles().contains("ROLE_USER")) {
         user.getRoles().add("ROLE_USER");
      }
      
      //isActive
      user.setActive(active);
      
      //password
      if (user.getPassword() == null
            || !StringUtils.hasText(user.getPassword())) {
         user.setPassword(TokenGenerator.generateRandom(70));
      }
      user.setPassword(PasswordEncoder.encode(user.getPassword()));
      
      return addUser(user);
   }
   
   /* (non-Javadoc)
    * @see com.h2.tac.server.user.IUserService#addUser(com.h2.tac.user.model.User)
    */
   @Override
   public User addUser(User user) {
      return getUserDao().addUser(user);
   }
   
   public Collection<User> getUsers() {
      return getUserDao().getUsers().values();
   }
   
   /**
    * An extension to the ACEGI User that provides wrapping of a DAO
    * User and convenience methods.
    * 
    */
   public static class UserExt 
         extends org.springframework.security.core.userdetails.User {
      
      private static final long serialVersionUID = 1L;
      
      private User _details;
      
      public UserExt(
            User details, 
            String username, 
            String creds,
            boolean enabled,
            boolean accountNonExpired,
            boolean credentialsNonExpired,
            boolean accountNonLocked,
            Collection<GrantedAuthority> auths) {
         
         super(username, creds, enabled, accountNonExpired,
               credentialsNonExpired, accountNonLocked, auths);
         setDetails(details);
      }
      
      public static UserExt createUserExt(User user) {
         return createUserExt(user, user.getPassword());
      }
      
      public static UserExt createUserExt(User user,
            String password) {
         
         Collection<GrantedAuthority> auths = Collections2.transform(user
               .getRoles(), new Function<String, GrantedAuthority>() {
            public GrantedAuthority apply(String from) {
               return new SimpleGrantedAuthority(from);
            }
         });
         return createUserExt(user, password, auths);
      }
      
      public static UserExt createUserExt(User user,
            String password, Collection<GrantedAuthority> auths) {
         
         return new UserExt(
               user, //details
               user.getName(), //name
               password,  //creds
               user.isActive(), //enabled
               true, //accountNonExpired
               true, //credentialsNonExpired
               true, //accountNonLocked
               auths //auths
               );
      }

      public void setDetails(User details) {
         this._details = details;
      }

      public User getDetails() {
         return _details;
      }
      
   }

   /**
    * @return the userDao
    */
   public IUserDao getUserDao() {
      return _userDao;
   }

   /**
    * @param userDao the userDao to set
    */
   public void setUserDao(IUserDao userDao) {
      _userDao = userDao;
   }

   /**
    * @return the userListeners
    */
   public Collection<IUserListener> getUserListeners() {
      return _userListeners;
   }

   /**
    * @param userListeners the userListeners to set
    */
   public void setUserListeners(Collection<IUserListener> userListeners) {
      _userListeners = userListeners;
   }

}
